// Audio-only capture support for Library for 
// Streambox PVR codec..
//
// Created: October-10-2002
// serg@streambox.com

#define RECORD_BUFFER_SIZE      (320*10)
#define DATABLOCK_SIZE          (320*5)
#define MAX_BUFFERS                 2  // double buffer
#define MSG_LEN                   128

#ifndef _VC10EA_

#include "AudioCapture.h"
#include "../Common/capTypes.h"
#include "../Common/gvar.h"
#include "../../Echo/ec_sbx.h"	// echo cancelation
#include "gsm.h"

#include "../Common/AudioCompression.h"
#include "../Common/VideoCompression.h"

#include "AudioCaptureWindow.h"

#include <stdio.h>
 
DWORD    nRecordBufferPos = 0L;  // position in Record buffer  
DWORD    nRecordBufferLen = 0L;  // total data in Record buffer

BOOL TestOpenInputDevice(HWND hWnd, const TCHAR *szDriverName);

BOOL StartRecordTest  (HWND hWnd);
VOID StopRecordTest   (HWND hWnd);
VOID LoadDataBlock(WAVEHDR* lpwh);

VOID AllocBuffers();
VOID CleanUpBuffers();


void OnInblock(WPARAM wParam, LPARAM lParam);

void DoZeroFrameCompress();

extern DWORD bmpFormat;

static int nAODriversCnt = 0;
static char szAODrivers[10][80];

#else //ndef _VC10EA_

DWORD    nRecordBufferPos;
DWORD    nRecordBufferLen;
int nAODriversCnt;
char szAODrivers[10][80];

#endif //ndef _VC10EA_

enum Status eStatus;
char msg[MSG_LEN+1];
HWAVEIN  hwi;
LPWAVEHDR  whin[MAX_BUFFERS];
VOID*    pRecordBuffer;  
BYTE *ptrZeroFrame;


int InitializeAODrivers()
{
	WAVEINCAPS   wic;
	UINT         nMaxDevices = waveInGetNumDevs();
	UINT         nDevId;
	MMRESULT     rc;  

	hwi = NULL;

	if(nMaxDevices > 10)
		nMaxDevices = 10;

	for (nDevId = 0; nDevId < nMaxDevices; nDevId++)                         
	{                                                               
	   rc = waveInGetDevCaps(nDevId, &wic, sizeof(wic)); 
	   if (rc == MMSYSERR_NOERROR)                                  
	   {
		   strncpy(szAODrivers[nDevId], wic.szPname, sizeof(szAODrivers[nDevId]));
		   nAODriversCnt++;
	   }
	}
	return nAODriversCnt;
}


/*****************************************************************************
*  TestOpenInputDevice
*
*
*
******************************************************************************/

BOOL TestOpenInputDevice(HWND hWnd, const TCHAR *szDriverName)
{
   WAVEINCAPS   wic;                                               
	WAVEFORMATEX wfx;
	UINT         nDevId;
	MMRESULT     rc;                                                
	UINT         nMaxDevices = waveInGetNumDevs();                  
	
//	hwi = NULL;
	nRecordBufferPos = 0L;  // position in Record buffer
   nRecordBufferLen = 0L;  // total data in Record buffer
   eStatus = StatusOkay;     // reset status                                            
	       
   CopyMemory(&wfx, &AudioFormat, sizeof(WAVEFORMATEX));
   
	for (nDevId = 0; nDevId < nMaxDevices; nDevId++)                         
	{                                                               
	   rc = waveInGetDevCaps(nDevId, &wic, sizeof(wic)); 
	   if (rc == MMSYSERR_NOERROR)                                  
	   {
		   if((strcmp(szDriverName, wic.szPname) != 0))
		   {
			   continue;
		   }
          // attempt 8Khz 16 bits mono if device capable
      
          // open waveform input device
          //...........................

	       rc = waveInOpen(&hwi, nDevId, &wfx,(DWORD)hWnd, (DWORD)hWnd,                   
	                       CALLBACK_WINDOW );                                          
	       
	       if (rc == MMSYSERR_NOERROR)                              
	           break;                                               
	       else                                                     
	       {     
			   // attempt ot open in 22kHz stereo mode (default mode)
				wfx.nChannels      = 1;  // use DevCaps # channels
				wfx.nSamplesPerSec = 22050;  // 22.05 kHz (22.05 * 1000)
				wfx.wFormatTag      = WAVE_FORMAT_PCM;                          
       			wfx.wBitsPerSample  = 16;                                        
      			wfx.nBlockAlign     = wfx.nChannels * wfx.wBitsPerSample / 8;   
      			wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;     
      			wfx.cbSize          = 0;
		       rc = waveInOpen(&hwi, nDevId, &wfx, (DWORD)hWnd, (DWORD)hWnd,                   
	                       CALLBACK_WINDOW );                                          

			   if(rc == MMSYSERR_NOERROR)
			   {
				   break;
			   }
	           waveInGetErrorText(rc, msg, MSG_LEN),                    
#ifdef _VC10EA_
			::
#endif //def _VC10EA_
	           MessageBox(hWnd, msg, NULL, MB_OK);
	           return(FALSE);
	       }                                                        
	   }                                                            
	}                                                               
	
   // device not found, error condition
   //..................................

	if (hwi == NULL)
	{
       eStatus = StatusError;
	    return(FALSE);
   }


   return(TRUE);
}


/*****************************************************************************
*  StartRecordTest
*
*
*
******************************************************************************/

BOOL StartRecordTest(HWND hWnd)
{
	MMRESULT rc;
   int      i;
   MMTIME   mmtime;

	// prepare buffer blocks and add to input queue
	//.............................................
	for (i = 0; i < MAX_BUFFERS; i++)                                        
	{                                                               
	   rc = waveInPrepareHeader(hwi, whin[i], sizeof(WAVEHDR));
	                                                                
	   // add buffers to the input queue                            
	                                                                
	   if (rc == MMSYSERR_NOERROR)
	   {
	       rc = waveInAddBuffer(hwi, whin[i], sizeof(WAVEHDR));
		   //MessageBox(hWnd, "Added buffer", "After prep", MB_OK);
	   }
	                                                                
	   if (rc != MMSYSERR_NOERROR)                                  
	   {                                                            
	       waveInGetErrorText(rc, msg, MSG_LEN),                        
#ifdef _VC10EA_
		::
#endif //def _VC10EA_
	       MessageBox(hWnd, msg, "Error in Preparartion of Wave Headers", MB_OK);
          StopRecordTest(hWnd);  // free allocated memory
		  return FALSE;
	   }                                                            
	}                                                               
	                                                                
	// start recording                                              
	//................                                              
	                                                                
	rc = waveInStart(hwi);
	 
	// test waveInGetPosition() function

   mmtime.wType = TIME_SAMPLES;

   rc = waveInGetPosition(hwi, &mmtime, sizeof(MMTIME));

   if (rc != MMSYSERR_NOERROR)                                  
	{                                                            
	    waveInGetErrorText(rc, msg, MSG_LEN),                        
#ifdef _VC10EA_
		::
#endif //def _VC10EA_
	    MessageBox(hWnd, msg,"Error in GetPosition of WaveIn", MB_OK);
		return FALSE;
	}         

   return TRUE;
}

/*****************************************************************************
*  StopRecordTest
*
*
*
******************************************************************************/

VOID StopRecordTest(HWND hWnd)
{
	//MMRESULT rc;
	int i;
	HWAVEIN hwicopy;
	//int nReply;
	//BOOL testopen;
	
	// stop recording                                               
	//...............                                               

	//waveInReset Stops the device and sets all the pending buffers to zero.
	waveInReset(hwi);
	
	// Unprepare headers                                                      
	//..................
	
	for (i = 0; i < MAX_BUFFERS; i++)
		waveInUnprepareHeader(hwi, whin[i], sizeof(WAVEHDR));
	
	hwicopy = hwi;
	hwi = NULL;
	waveInClose(hwicopy);
	//AudioFile->Close();
//	MessageBox(hWnd, "Finished Recording and Saved WAV file", "Done", MB_OK);
	
	// I do not know if cleanup is really necessary since the buffers were dynamically allocated.
	// I think the memory is freed once the process is destroyed.

//CleanUpBuffers();  
	
	
}



/*****************************************************************************
*  LoadDataBlock
*
*
*
******************************************************************************/

VOID LoadDataBlock(WAVEHDR* lpwh)
{
   DWORD nLen;

   if (nRecordBufferPos < nRecordBufferLen)
   {
       nLen = nRecordBufferLen - nRecordBufferPos;

       if (nLen > DATABLOCK_SIZE)
           nLen = DATABLOCK_SIZE;
   
       memcpy(lpwh->lpData, ((char*)pRecordBuffer+nRecordBufferPos), nLen);
       lpwh->dwBufferLength = nLen;
       nRecordBufferPos  += nLen;
   }
   else
       eStatus = StatusDone;
}



/*****************************************************************************
*  AllocBuffers
*
*
*
******************************************************************************/

VOID AllocBuffers()
{
	int i;
	
	// allocate two WAVEHDR buffer blocks
	//...................................
	
	
	 
	for (i = 0; i < MAX_BUFFERS; i++)                                        
	{         
		whin[i] = (WAVEHDR*) malloc( sizeof(WAVEHDR) );
		if (whin[i])                                                  
		{                                                            
			whin[i]->lpData = (char*)malloc(DATABLOCK_SIZE);//new char[DATABLOCK_SIZE];                       
			whin[i]->dwBufferLength = DATABLOCK_SIZE;
			whin[i]->dwFlags = 0;
		} 
	}
	
	// allocate Record buffer - enough space to hold
	// ten data buffer blocks of waveform sound data
	//................................................
	
	pRecordBuffer = (BYTE*)malloc(RECORD_BUFFER_SIZE);

	ptrZeroFrame = (BYTE*)malloc(g_cs.w * g_cs.h * 3);
	ZeroMemory(ptrZeroFrame, g_cs.w * g_cs.h * 3);
} 

/*****************************************************************************
*  CleanUpBuffers
*
*
*
******************************************************************************/

VOID CleanUpBuffers()
{

	int i;
	
	// free the WAVEHDR buffer blocks
	//...............................
	
	for (i = 0; i < MAX_BUFFERS; i++)                                        
	{
		if (whin[i] != NULL)
		{
			free( whin[i]->lpData); 
			free(whin[i]);
			whin[i] = NULL;
		}
	}
	
	// free Record buffer
	//.....................
	
	free(pRecordBuffer);
	pRecordBuffer = 0;

}

int InitializeAOMode()
{
	int i;

	
//	AudioFile = new CFile("C:\\RawAudio\\data.raw", CFile::modeCreate | CFile::modeWrite);

	hWndC = AudioCaptureWindow_Create(g_cs.w, g_cs.h, hwndParent);
	if (hWndC==0) {
		printf("Cannot open capture window");
		return 0;
	}

//	AudioCompression_Create();

	for (i = 0; i < MAX_BUFFERS; i++)                                        
		whin[i] = NULL;	
	
	eStatus = StatusOkay;
	AllocBuffers();

	// set zero bitmap BI_RGB mode
	bmpFormat = BI_RGBH;

/*
	if(!TestOpenInputDevice(hWndC, g_cs.szDriverName))
	{
		CleanUpBuffers();
	}
*/    
 
	return 1;
} 

void OnInblock(WPARAM wParam, LPARAM lParam)
{
	// Received data buffer block with recorded data,
	// Copy to Record buffer for use later
	//...............................................
	
	MMRESULT  rc;
	MMTIME    mmtime;
	LPWAVEHDR lpwh = (LPWAVEHDR)lParam;
	
	//The following if block test if the buffer used for file I/O is full.  
//If full then write the buffer to disk and then zero it out.
//If not totally full then add the received buffer to the file I/O buffer.
//After that is done send the buffer back to the device.
	
	if((hwi != NULL) && (lpwh->dwFlags & WHDR_DONE) && (eStatus == StatusOkay))
	{
		if ((nRecordBufferPos + (lpwh->dwBytesRecorded)) >= RECORD_BUFFER_SIZE) 
		{
			int i;
//			AudioFile->WriteHuge((BYTE*)pRecordBuffer, nRecordBufferLen);
			//MessageBox(msg, "Got a input buffer back 1", MB_OK);
			/*	
			for(i=0; i < nRecordBufferLen/2; i++)
			{
				int v = sin((i&255)*3./255)*32000-15000;
				((short*)pRecordBuffer)[i] = (short)v;
			}*/
			AudioCompressCallback(hWndC, (BYTE*)pRecordBuffer, nRecordBufferLen);

			nRecordBufferPos = 0L;
			nRecordBufferLen = 0L;
		}
		
		// copy data buffer block to Record buffer
		//..........................................
		
		memcpy( ((BYTE*)pRecordBuffer+nRecordBufferPos), lpwh->lpData, lpwh->dwBytesRecorded);
		//MessageBox(msg, "Got a input buffer back", MB_OK);
		nRecordBufferPos += lpwh->dwBytesRecorded;
		nRecordBufferLen += lpwh->dwBytesRecorded;
		
		mmtime.wType = TIME_BYTES;
		waveInGetPosition(hwi, &mmtime, sizeof(MMTIME));
		//wsprintf(msg, "%s - Recorded %ld bytes", "", mmtime.u.cb);
		//SetWindowText(hwndParent, msg);
		
		// reuse data buffer block
		//........................ 
		
		rc = waveInPrepareHeader(hwi, lpwh, sizeof(WAVEHDR));       
		
		if (rc == MMSYSERR_NOERROR)                                  
			rc = waveInAddBuffer(hwi, lpwh, sizeof(WAVEHDR));       
		
		if (rc != MMSYSERR_NOERROR)
		{                                                            
			waveInGetErrorText(rc, msg, MSG_LEN);
#ifdef _VC10EA_
			::
#endif //def _VC10EA_
			MessageBox(hWndC, msg, "Could not prepare buffer for refresh", MB_OK);
			eStatus = StatusError;    // signal to shut down the process
		}
	}
}

//----------- set call back function
void capAudio_SetCallback(CAP_PROCESS proc)
{
	cap_process=proc;
}




void AO_Stop()
{
	eStatus = StatusDone;
	StopRecordTest(hWndC);
	AudioCaptureWindow_SetTimerCallback(0, NULL);
	AudioCaptureWindow_AddHandler(0, NULL);
}

int AO_Start()
{
	eStatus = StatusOkay;

#ifndef _VC10EA_
	AudioCaptureWindow_SetTimerCallback(1, DoZeroFrameCompress);
	AudioCaptureWindow_AddHandler(WIM_DATA, OnInblock);
#else //ndef _VC10EA_
	AudioCaptureWindow_SetTimerCallback(1, NULL);
	AudioCaptureWindow_AddHandler(WIM_DATA, NULL);
#endif //ndef _VC10EA_

	if(!TestOpenInputDevice(hWndC, g_cs.szDriverName))
	{
		CleanUpBuffers();
	}

	if(!StartRecordTest(hWndC))
	{
		CleanUpBuffers();
		return 0;
	}

	return 1;
}

void AO_Abort()
{

	eStatus = StatusDone;
	StopRecordTest(hWndC);

	AudioCaptureWindow_SetTimerCallback(0, NULL);
	AudioCaptureWindow_AddHandler(0, NULL);

//	AudioCompression_Destroy();
	CleanUpBuffers();
	if(ptrZeroFrame)
	{
		free(ptrZeroFrame);
		ptrZeroFrame = 0;
	}
}

BOOL AO_SetVideoSource()
{
	return TRUE;
}

BOOL AO_SetAudioStatus(BOOL bAudioStatus)
{

	if(bAudioStatus)
	{
		return (hwi == NULL) ? AO_Start() : TRUE;
	}

	if(hwi != NULL) AO_Stop();

	return TRUE;
}

BOOL AO_SetVideoStatus(BOOL bVideoStatus)
{
	if(bVideoStatus)
	{
#ifndef _VC10EA_
		return AudioCaptureWindow_SetTimerCallback(1, DoZeroFrameCompress);
#else //ndef _VC10EA_
		return AudioCaptureWindow_SetTimerCallback(1, NULL);
#endif //ndef _VC10EA_
	}
	return AudioCaptureWindow_SetTimerCallback(0, NULL);
}

BOOL IsAODriver( const TCHAR *szDriverName)
{ 

	int i = 0;
	for (i = 0; i < nAODriversCnt; i++)                         
	{                                                               
		if((strcmp(szDriverName, szAODrivers[i]) == 0))
		{
		 return TRUE;
		}
	}
	return FALSE;
}


const TCHAR* GetAODriverNameByID(int nID)
{
	if(nID < nAODriversCnt)
	{
		return szAODrivers[nID];
	}
	return NULL;
}

void DoZeroFrameCompress()
{
	if(hWndC && cap_process && CaptureEnable && CaptureEnableVideo) 
		VideoCompression_SetStillImage(ptrZeroFrame, 4);
}
